package com.apes.scm.voucher.service.cost;

import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.util.Arith;
import com.apes.framework.util.DateUtil;
import com.apes.framework.util.LogicalUtil;
import com.apes.scm.account.account.model.AccountingMethod;
import com.apes.scm.account.account.model.InventoryBatch;
import com.apes.scm.account.account.model.InventoryMode;
import com.apes.scm.account.account.model.InventoryVo;
import com.apes.scm.account.account.service.InventoryService;
import com.apes.scm.masterdata.product.service.UomService;
import com.apes.scm.voucher.model.Voucher;
import com.apes.scm.voucher.model.VoucherInventory;
import com.apes.scm.voucher.model.VoucherItem;
import com.apes.scm.voucher.repository.VoucherItemRepository;
import com.apes.scm.voucher.service.BusinessType;
import com.apes.scm.voucher.service.ValuationService;
import com.apes.scm.voucher.service.VoucherService;
import com.apes.scm.voucher.service.VoucherType;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.groupingBy;

/**
 * 功能：成本核算/价值评估
 *
 * @author xul
 * @create 2018-10-18 11:25
 */
public abstract class AbstractCostMethod extends DomainService implements CostMethod {

    @Autowired
    ValuationService valuationService;

    @Autowired
    InventoryService inventoryService;

    @Autowired
    UomService uomService;


    protected String[] BusinessTypes;

    /**
     * 功能：业务单据类型
     */
    @Override
    public String[] getBusinessTypes() {
        if (BusinessTypes == null || BusinessTypes.length == 0) {
            throw new RuntimeException("业务单据类型不能为空！");
        }
        return BusinessTypes;
    }


    /**
     * 价值评估
     *
     * @param voucher
     * @param evaluate                价值评估扩展方法
     * @param createInventoryVoExtend 创建InventoryVo扩展方法
     * @return
     */
    protected Collection<VoucherInventory> costing(Voucher voucher, Consumer<CostContext> evaluate, BiConsumer<VoucherItem, InventoryVo> createInventoryVoExtend) {
        Set<VoucherInventory> voucherInventories = new HashSet();
        voucher.getVoucherItems().forEach((voucherItem) -> {
            InventoryVo vo = createInventoryVo(voucherItem, createInventoryVoExtend);
            CostContext context = new CostContext(voucherItem, vo);
            evaluate.accept(context);
            AtomicReference<Double> cost = new AtomicReference<>((double) 0);
            context.getInventoryVos().stream().forEach((inventoryVo) -> {
                InventoryBatch outInventoryBatch = inventoryVo.getOutInventoryBatch();
                double qty = voucherItem.sign() * inventoryVo.getQty();
                VoucherInventory voucherInventory = createVoucherInventory(voucherItem, outInventoryBatch, qty);
                voucherInventories.add(voucherInventory);
                cost.updateAndGet(v -> v + Arith.mul(voucherInventory.getTaxCostPrice(), voucherInventory.getProductQty()));
            });
            cost.updateAndGet(v -> Arith.div(v, voucherItem.getProductQty(), 4));
            voucherItem.setTaxCostPrice(cost.get());
            SpringManager.getBean(VoucherItemRepository.class).save(voucherItem);
        });
        return valuationService.save(voucherInventories);
    }

    /*
     * 获取价值库存（非采购流程,采购为产生价值库存的来源）
     * @Params
     * mapper： 需要对vo实体重新赋值等操作
     * */
    protected InventoryVo createInventoryVo(VoucherItem voucherItem, BiConsumer<VoucherItem, InventoryVo> mapper) {
        InventoryVo vo = new InventoryVo();
        vo.setCompany(voucherItem.getVoucher().getCompany());
        vo.setProduct(voucherItem.getProduct());
        vo.setUom(voucherItem.getUom());
        vo.setQty(voucherItem.getProductQty());
        vo.setInInventories(voucherItem.makeInventoryBatches());
        if (mapper != null) {
            mapper.accept(voucherItem, vo);
        }
        return vo;
    }

    protected boolean isSureInventories(InventoryVo inventoryVo) {
        return inventoryVo.getInInventories() != null && !inventoryVo.getInInventories().isEmpty();
    }

    /*
     * 创建价值评估
     * */
    protected VoucherInventory createVoucherInventory(VoucherItem voucherItem, InventoryBatch inventoryBatch, double changeQty) {
        VoucherInventory voucherInventory = new VoucherInventory();
        voucherInventory.setCompany(voucherItem.getVoucher().getCompany());
        voucherInventory.setVoucher(voucherItem.getVoucher());
        voucherInventory.setVoucherItem(voucherItem);
        voucherInventory.setPartner(voucherItem.getVoucher().getPartner());
        voucherInventory.setTax(voucherItem.getTax());
        voucherInventory.setProduct(voucherItem.getProduct());
        voucherInventory.setUom(voucherItem.getUom());
        voucherInventory.setPriceUnit(voucherItem.getPriceUnit());
        voucherInventory.changeProductQty(changeQty);
        voucherInventory.setInventoryBatch(inventoryBatch);
        voucherInventory.setInventorySupplier(inventoryBatch.getSupplier());
        voucherInventory.setInventoryCooperation(inventoryBatch.getCooperation());
        voucherInventory.setInventoryDept(inventoryBatch.getDept());
        voucherInventory.setInventoryContract(inventoryBatch.getContract());
        double taxCostPrice = inventoryBatch.getUom().computePrice(inventoryBatch.getTaxCostPrice(), voucherItem.getUom());
        voucherInventory.setTaxCostPrice(taxCostPrice);
        voucherInventory.setAccountingDate(DateUtil.getDate(DateUtil.SHOW_DATE_FORMAT));
        voucherInventory.setDept(voucherItem.getVoucher().getDept());
        return voucherInventory;
    }

    /**
     * 寄售库存，做供应商寄售凭证
     **/
    protected void supplierConsume(Voucher voucher, Collection<VoucherInventory> voucherInventories) {
        voucherInventories.stream()
                .filter(inventory -> !inventory.isTemporary() && inventory.getInventoryCooperation().getInventoryMode().equals(InventoryMode.IM_CONSIGN))
                .collect(groupingBy(VoucherInventory::createGroupingKey))
                .forEach((groupingKey, valuations) -> {
                    VoucherInventory vi = valuations.stream().findFirst().get();
                    Voucher consignVoucher = new Voucher();
                    consignVoucher.setCompany(voucher.getCompany());
                    consignVoucher.setPartner(vi.getInventorySupplier());
                    consignVoucher.setVoucherType(VoucherType.SUPPLIER_CONSUME);
                    consignVoucher.setInventoryMode(vi.getInventoryCooperation().getInventoryMode());
                    consignVoucher.setAccountingMethod(vi.getInventoryCooperation().getAccountingMethod());
                    consignVoucher.setDept(vi.getInventoryDept());
                    consignVoucher.setContract(vi.getInventoryContract());
                    consignVoucher.setSource(voucher.getId());
                    consignVoucher.setSourceBusinessType(voucher.getInnerBusinessType());
                    consignVoucher.setOrigin(voucher.getOrigin());
                    consignVoucher.setOriginBusinessType(voucher.getOriginBusinessType());
                    consignVoucher.setReturned(voucher.isReturned());
                    consignVoucher.setBusinessType(voucher.isReturned() ? BusinessType.SUPPLIER_CONSUME_RETURN_VOUCHER : BusinessType.SUPPLIER_CONSUME_ORDER_VOUCHER);
                    consignVoucher.setInvoiced(true);
                    consignVoucher.setCustomerExpand(voucher.getCustomerExpand());

                    consignVoucher.setVoucherItems(
                            valuations.stream().map(voucherInventory -> {
                                VoucherItem voucherItem = new VoucherItem();
                                voucherItem.setProduct(voucherInventory.getProduct());
                                voucherItem.setUom(voucherInventory.getUom());
                                voucherItem.setSourceItemId(String.valueOf(voucherInventory.getVoucherItem().getId()));
                                voucherItem.setOriginItemId(voucherInventory.getVoucherItem().getOriginItemId());
                                voucherItem.setTax(voucherInventory.getInventoryBatch().getTax());
                                voucherItem.setInventoryBatch(voucherInventory.getInventoryBatch());
                                voucherItem.setProductQty(voucherInventory.getProductQty());
                                if (LogicalUtil.in(vi.getInventoryCooperation().getAccountingMethod(), AccountingMethod.AM_PURCHASE, AccountingMethod.AM_REAL)) {
                                    voucherItem.setPriceUnit(voucherInventory.getTaxCostPrice());
                                } else {
                                    /**价格与 cooperation.getAccountingMethod() 有关，相关方法待实现（待实现）**/
                                    throw new RuntimeException("销售倒扣方法待实现！");
                                }
                                voucherItem.setVoucher(consignVoucher);
                                return voucherItem;
                            }).collect(Collectors.toSet())
                    );
                    SpringManager.getBean(VoucherService.class).save(consignVoucher);
                });
    }

}
